/******************************************************************************
 * Copyright (C), 2014, BeeIoT Tech. Co., Ltd. 
 * FileName:        conv.c
 * Author:          lvxin 
 * Version :        1.0
 * Date:            2014-12-05 11:39:09
 * Description:
 * Function List:   1. -------
 * History:
 * <author>     <time>                <version >  <desc> 
 * lvxin        2014-12-05 11:39:09   1.0         build this moudle 
 *****************************************************************************/

/******************************************************************************
 *  *INCLUDES
 *  */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

/******************************************************************************
 *  *FUNCTION DECLARES
 *  */
static int _ucs2_ch_to_utf8(const uint16_t uc, char *obuf, size_t outbytes);

/******************************************************************************
 *  *LOCAL FUNCTIONS
 *  */

/******************************************************************************
 * Function:        _ucs2_ch_to_utf8()
 * Description:     把ucs2字符转换为utf8字符
 * Calls:            
 * Called By:       conv_pdu_to_utf8()
 * Input:           uc --uint16_t类型的ucs2字符
 * Return:          转换为utf8后占用的字节数
 * Others:           
 *****************************************************************************/
static int _ucs2_ch_to_utf8(const uint16_t uc, char *obuf, size_t outbytes)
{
    int follow = 0;
    char *obuf_end = obuf + outbytes;

    if (uc < 0x80) 
    {
        follow = 0;
    }
    else if (uc < 0x800)
    {
        follow = 1;
    }
    else
    {
        follow = 2;
    }

    if (obuf + follow < obuf_end)
    {
        switch (follow)
        {
            case 0 :
                *obuf++ = 0xFF&uc;
                break;
            case 1 :
                *obuf++ = (uc>>6)|0xC0;
                *obuf++ = (uc&0x3f)|0x80;
                break;
            case 2 :
                *obuf++ = (uc>>12)|0xE0;
                *obuf++ = ((uc>>6)&~(0xf<<6)) |0x80;
                *obuf++ = (uc&0x3f)|0x80;
                break;
            default :
                break;
        }
    }
    else
    {
        return 0;
    }
    return follow + 1;
}
/******************************************************************************
 *  *PUBLIC FUNCTIONS
 *  */

/*******************************************************************************
 * Function:      conv_PDU_tel_to_normal_tel()
 * Description:   把PDU格式的电话号码转换为正常电话号码
 * Calls:         
 * Called By:     
 * Input:         src   --原始电话号字符串
 *                len   --号码长度
 * Return:        转换后的电话号码
 * Others:        使用后需要free释放掉
 ******************************************************************************/
char* conv_PDU_tel_to_normal_tel(char *src, int len)
{
    int i;
    char *tel = NULL;
    int index = 1;
    if (src != NULL && len != 0)
    {
        tel = (char *) calloc (1, 25 * (1 + len / 25));
        for (i = 0; i < len ; i ++)
        {
            tel[index] = src[i];
            if (tel[index] == 'F')
            {
                tel[index] = '\0';
            }

            if (index % 2 == 1)
            {
                index -= 1;
            }
            else
            {
                index += 3;
            }
        }
    }
    return tel;
}

/******************************************************************************
 * Function:        conv_hex_char_to_dec_value()
 * Description:     得到十六进制字符对应的十进制数值
 * Calls:            
 * Called By:        
 * Input:           ch --十六进制字符
 * Return:          十进制数字
 * Others:           
 *****************************************************************************/
uint8_t conv_hex_ch_to_bin(char ch)
{
    if (ch >= 'a' && ch <='f')
    {
        return (ch - 'a') + 10;
    }
    else if (ch >= 'A' && ch <='F')
    {
        return (ch - 'A') + 10;
    }
    else if (ch >= '0' && ch <= '9')
    {
        return (ch - '0');
    }
    else 
    {
        return 0;
    }
}

/******************************************************************************
 * Function:        conv_PDU_date_to_CCLK_date()
 * Description:     把PDU格式的时间字符串转换为CCLK可用的时间字符串
 * Calls:           calloc
 * Called By:        
 * Input:           PDU_string  --PDU字符串
 * Return:          CCLK可用的字符串
 * Others:          使用后需要free释放内存
 *****************************************************************************/
char * conv_PDU_date_to_CCLK_date (char *PDU_string)
{
    uint8_t YY = 0;
    uint8_t MM = 0;
    uint8_t DD = 0;
    uint8_t hh = 0;
    uint8_t mm = 0;
    uint8_t ss = 0;
    int8_t  zz = 0;
    char *ret = NULL;

    if (strlen (PDU_string) >= 14)
    {
        YY = (PDU_string[1] - '0')*10 + (PDU_string[0] - '0');
        MM = (PDU_string[3] - '0')*10 + (PDU_string[2] - '0');
        DD = (PDU_string[5] - '0')*10 + (PDU_string[4] - '0');
        hh = (PDU_string[7] - '0')*10 + (PDU_string[6] - '0');
        mm = (PDU_string[9] - '0')*10 + (PDU_string[8] - '0');
        ss = (PDU_string[11] - '0')*10 + (PDU_string[10] - '0');
        zz = (int8_t)((PDU_string[13] - '0')*10 + (PDU_string[12] - '0'));

        ret = (char *)calloc(1,25);
        if (ret == NULL)
        {
            return ret;
        }
        sprintf (ret, "\"%02d/%02d/%02d,%02d:%02d:%02d%+02d\"",
                YY % 100, 
                MM % 13, 
                DD % 32, 
                hh % 24, 
                mm % 60, 
                ss % 60, 
                zz);
    }
    return ret;
}

/*******************************************************************************
 * function:      conv_pdu2asc()
 * description:   把pdu格式字符串转换为ascii字符串
 * calls:         conv_hex_char_to_dec_value() 
 * called by:     sm_data_parse()
 * input:         src  --pdu格式字符串
 *                dest --输出ascii字符串
 * return:        void
 * others:         
 ******************************************************************************/
void conv_pdu2asc(const char *src, char *dest, int len)
{
    int i;
    uint32_t t = 0;
    int po = 0;
    /*int srclen = strlen(src);*/

    for (i = 0; i < len; i += 4)
    {
        t = 0;
        t =((uint32_t)conv_hex_ch_to_bin(src[i]) << 12) |
            ((uint32_t)conv_hex_ch_to_bin (src[i+1]) << 8)|
            ((uint32_t)conv_hex_ch_to_bin (src[i+2]) << 4) |
            ((uint32_t)conv_hex_ch_to_bin (src[i+3]));
        dest[po] = t;
        po ++;
    }
    dest[po] = '\0';

    return ;
}

/*******************************************************************************
 * Function:      conv_toupper()
 * Description:   把字符串全变成大写
 * Calls:         
 * Called By:      
 * Input:         msg   --字符串
 *                len   --字符串长度
 * Return:        void
 * Others:        None
 ******************************************************************************/
void conv_toupper(char *msg, int len)
{
    int i;
    for (i = 0; i < len; i ++)
    {
        if (msg[i] >= 'a' && msg[i] <= 'z')
        {
            msg[i] = msg[i] - 32;
        }
    }
}

int conv_ucs2_to_utf8(const char *ibuf, size_t inbytes,
        char *obuf, size_t *outbytes)
{
    uint16_t uc = 0;
    const char *ibuf_end, *obuf_end;
    size_t out_bytes = 0;
    int follow = 0;
    uint8_t ch = 0;
    int shift = 0;
    if (ibuf!=NULL && obuf!=NULL && outbytes!=NULL)
    {
        ibuf_end = ibuf+inbytes;
        obuf_end = obuf+*outbytes;
        while(&ibuf[1]<ibuf_end && obuf<obuf_end){

            uc = (0xFF&*ibuf++);
            uc = (0xFF&*ibuf++)|(uc<<8);
            if (uc < 0x80){
                *obuf++ = (uc);
                out_bytes ++;
                follow = 0;
            }else if (uc < 0x800){
                *obuf++ = (uc>>6)|0xC0;
                out_bytes ++;
                follow = 1;
            }else {
                /* assert(uc<=0xFFFF); */
                *obuf++ = (uc>>12)|0xE0;
                out_bytes ++;
                follow = 2;
            }
            if (&obuf[follow] > obuf_end){
                /*no output buffer */
                break;
            }
            for (follow--;follow>=0;follow--){
                shift = follow*6;
                ch = uc>>shift;
                *obuf++ = (ch&0x3F)|0x80;
                out_bytes ++;
            }
            /**outbuf = obuf;*/
            /**inbuf = ibuf;*/
        }
        /**outbytes = obuf_end - *outbuf;*/
        /**inbytes = ibuf_end - *inbuf;*/
    }
        *outbytes = out_bytes;
    return 0;
}


//将PDU字符串转换为二进制数组
/******************************************************************************
 * Function:        conv_pdu_to_bin()
 * Description:     把PDU短信的字符串转换为数组数据
 * Calls:           conv_hex_ch_to_bin()
 * Called By:        
 * Input:           char *string        --pdu字符串
 *                  char *array        --保存二进制数据的缓冲区
 *                  size_t *size     --缓冲区大小,执行完
 *                                        后会返回处理后生成的数据个数
 * Return:           
 * Others:          size_t *size执行完后会返回处理后生成的数据个数
 *****************************************************************************/
int conv_pdu_to_bin(const char *string, char *array, size_t *size)
{
    int i = 0;
    int pdu_len = 0;
    int out_bytes = 0; //转换的字节
    size_t arr_len = *size - 1;   //缓冲区大小
    if (string && size && array)
    {
        pdu_len = strlen (string); 
        for (i = 0; i < pdu_len && i+1 < pdu_len; i += 2)
        {
            if (out_bytes < arr_len)
            {
                array[out_bytes] = conv_hex_ch_to_bin(string[i]) << 4 | conv_hex_ch_to_bin(string[i+1]);
                out_bytes ++;
            }
            else
            {
                break;
            }
        }
        *size = out_bytes + 1;
        memset(array + out_bytes, 0, arr_len - out_bytes + 1);
    }
    else return -1;
    return 0;
}

/******************************************************************************
 * Function:        conv_pdu_to_utf8()
 * Description:     把PDU短信的字符串转换为utf8编码的字符串
 * Calls:           _ucs2_ch_to_utf8()
 * Called By:        
 * Input:           char *pdu_string --pdu字符串
 *                  char *outbuf        --保存utf8字符串的缓冲区
 *                  size_t *bufsize     --保存utf8字符串的缓冲区大小,执行完
 *                                        后会返回处理后生成的字符串尺寸
 * Return:           
 * Others:          bufsize     --保存utf8字符串的缓冲区大小,执行完
 *                              后会返回处理后生成的字符串尺寸
 *****************************************************************************/
int conv_pdu_to_utf8(const char *str, int len, char *outbuf, size_t *outbytes)
{
    size_t obytes = 0;
    char *out_end = NULL; //缓冲区结尾
    char *in_end = NULL; 
    uint16_t uc = 0;
    int o = 0;

    if (str && len && outbuf && outbytes)
    {
        obytes = *outbytes;
        out_end = outbuf + obytes;
        in_end = (char *)str + len;
        *outbytes = 0;

        while(str < in_end 
                && (str+4 <= in_end) 
                && outbuf < out_end
                )
        {
            uc = (conv_hex_ch_to_bin (*(str + 0)) << 12)
                + (conv_hex_ch_to_bin (*(str + 1)) << 8)
                + (conv_hex_ch_to_bin (*(str + 2)) << 4)
                + (conv_hex_ch_to_bin (*(str + 3)));

            o = _ucs2_ch_to_utf8(uc, outbuf, obytes - *outbytes);
            *outbytes += o;
            outbuf += o;

            str += 4;
            len -= 4;
        }
        memset (outbuf, 0, obytes - *outbytes);
    }
    else
    {
        return -1;
    }
    return len;
}

/******************************************************************************
 * Function:        conv_char_7bit_pdu_unpack()
 * Description:     把7bit的pdu数据解析为8bit并保存到outbin缓冲区中
 * Calls:           strtol()
 * Called By:        
 * Input:           str     --原始7bit的pdu字符串 
 * Input:           inlen   --原始7bit的pdu字符串长度 
 * Input:           outbin  --目标保存缓冲区
 * Input:           inlen   --缓冲区长度
 * Return:          
 * Others:           
 *****************************************************************************/
#define GN_BYTE_MASK ((1 << bits) - 1)
int conv_char_7bit_pdu_unpack(const char *str, int inlen, char *outbin, size_t *outsize)
{
    char tmp[3] = {0};
    uint8_t in_num = 0;
    unsigned char rest = 0x00;
    int bits = 7;
    char  *oend = NULL;
    const char *inend = NULL;

    if (outbin && outsize && str && inlen!=0)
    {
        oend = outbin + *outsize;
        inend = str + inlen;
        *outsize = 0;
        while(outbin<oend && str<inend)
        {
            tmp[0] = *str ++;
            tmp[1] = (str<=inend)?*str ++:0;
            in_num = (uint8_t)strtol(tmp, NULL, 16);
            tmp[0] = 0;
            tmp[1] = 0;
            tmp[2] = 0;

            (*outsize) ++;

            *outbin ++ = ((in_num & GN_BYTE_MASK) << (7 - bits)) | rest;
            rest = in_num >> bits;

            if (bits == 1) {
                *outbin ++= rest;
                bits = 7;
                rest = 0x00;
            } else {
                bits--;
            }
        }
    }
    else
    {
        return -1;
    }
    memset (outbin, 0, oend - outbin);
    return *outsize;
}

